/*
 * Copyright 2008-2009 Daniel Freitas
 * 
 * This file is part of Army Creator.
 * 
 * Army Creator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * Army Creator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with Army Creator. If not, see
 * <http://www.gnu.org/licenses/>.
 */
package com.googlecode.armycreator.datastore.genericdao.hibernate;

import com.googlecode.armycreator.datastore.genericdao.DaoQueryExecutor;
import com.googlecode.armycreator.datastore.genericdao.QueryMethodInvocation;
import org.hibernate.Query;
import org.hibernate.SessionFactory;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * {@link DaoQueryExecutor} implementation for hibernate. Execute hibernate mapped queries for methods invoked in the
 * dao interfaces.
 * <p/>
 * Queries must be mapped in hibernate and follow the naming convention
 * <code><i>[interface_fully_qualified_name].[method_name]</pre></i></code>. For example, if a GenericDao sub-interface
 * called <code>com.googlecode.armycreator.datastore.GameDao</code> has a method called
 * <code>listAllRostersByPointRange()</code>, then the correspondinf query must be named
 * <code>com.googlecode.armycreator.datastore.GameDao.listAllRostersByPointRange</code>.
 * 
 * @author <a href="mailto:daniel.mfreitas@gmail.com">Daniel Freitas</a>
 */
public class HibernateQueryExecutor implements DaoQueryExecutor {

    private final SessionFactory sessionFactory;

    /**
     * A {@link DaoQueryExecutor} implementation tailored to find execute hibernate named queries.
     * 
     * @param sessionFactory
     *            The hibernate {@link SessionFactory} to be used.
     */
    public HibernateQueryExecutor(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    /**
     * @see com.googlecode.armycreator.datastore.genericdao.DaoQueryExecutor#executeQueryForMethod(com.googlecode.armycreator.datastore.genericdao.QueryMethodInvocation)
     */
    @SuppressWarnings("unchecked")
    @Override
    public Object executeQueryForMethod(QueryMethodInvocation queryMethodInvocation) {
        Map parameters = queryMethodInvocation.getArguments();

        Object result;

        String queryName = queryMethodInvocation.getDaoInterfaceName() + "." + queryMethodInvocation.getMethodName();

        Query namedQuery = sessionFactory.getCurrentSession().getNamedQuery(queryName);

        setNamedQueryParameters(queryName, namedQuery, (Map) parameters);

        if (List.class.isAssignableFrom(queryMethodInvocation.getMethodReturnType())) {
            result = namedQuery.list();
        } else {
            result = namedQuery.uniqueResult();
        }

        return result;
    }

    // Extracted methods
    // -----------------------------------------------------------------------------------------------------------------
    private void setNamedQueryParameters(String queryName, Query namedQuery, Map<String, Object> arguments) {
        List<String> parameterNames = Arrays.asList(namedQuery.getNamedParameters());
        Set<Map.Entry<String, Object>> entries = arguments.entrySet();

        for (Map.Entry<String, Object> entry : entries) {
            if (!parameterNames.contains(entry.getKey())) {
                throw new IllegalArgumentException("The parameter named " + entry.getKey()
                        + " is not present in the hibernate query " + queryName);
            }

            namedQuery.setParameter(entry.getKey(), entry.getValue());
        }
    }
}
